package com.iaz.tech.tools.common.utils;

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 表达式
 */
public class ExpressionUtil {
    /** 
     * 日志
     */
	private final static Logger logger = (Logger) LoggerFactory.getLogger(ExpressionUtil.class);
    
    private final static String[] math_symbols = {
        "+,10", "-,10", "*,9", "/,9", "%,9", 
        "++,10", "--,10",
        "=,11",
        "^,8", "||,7", "&&,6", "!,5", 
        "|=,4", "&=,3",
        "|,5", "&,5", 
        ">>,8", "<<,8", ">>=,8", "<<=,8", 
        "==,12", "!=,11", 
        ">,2", "<,2", ">=,2", "<=,2",
        "(,1", "),1"
    };

    private final static double MATH_SYMBOL_EPSIILON = 0.00000001;
    /**
     * 获取数学运算符优先级
     * @param math_symbol
     * @return
     */
    public final static int getMathSymbolPriority(String math_symbol) {
        int priority = 0;
        try {
            for (String symbol : math_symbols) {
                if (symbol.startsWith(math_symbol)) {
                    priority = Integer.parseInt(symbol.split(",")[1]);
                    break;
                }
            }
        }catch(Exception e){
            logger.error("getMathSymbolPriority error", e);
        }
        return priority;
    }


    /**
     * 扫描表达式
     * @param expression
     * @return
     */
    public static String ScanExpression(String expression) {
        String result = "";
        try{
            if (UtilTools.IsNull(expression)) {
                return result;
            }
            List<String> words = StringUtil.splitStr(expression);
            if (words == null || words.isEmpty()) {
                logger.warn("ScanExpression words is null or empty");
                return result;
            }
            /* 判断是否调用表达式，用于进行 */
            List<String> value_stack_values = new ArrayList<>();
            List<String> operation_stack_values = new ArrayList<>();
            int cursor = 0;            

            /** 处理表达式, 用于扫描表达式，用于区分大小 */
            while((cursor < words.size())) {
                String word = words.get(cursor);
                if ( UtilTools.IsNull(word) ) {
                    cursor++;
                    continue;
                }
                if (StringUtil.isMathSymbol(word)) {
                    if (word.equals(")")) {
                        result = ExpressionUtil.calculateExpressionInnerPartForKuohao(value_stack_values, operation_stack_values, word);
                        cursor++;
                        continue;
                    }                    
                    // int _priority = ExpressionUtil.getMathSymbolPriority(word);
                    // if (_priority == -1) {
                    //     logger.warn("ScanExpression math_symbol is error, math_symbol = {}", word);
                    //     cursor++;
                    //     continue;
                    // }          
                    result = ExpressionUtil.calculateExpressionInnerPartForPriority(value_stack_values, operation_stack_values, word);
                    cursor++;
                    continue;
                } else {
                    value_stack_values.add(word);
                }
                cursor++;
            }
            /* 处理操作符栈 */
            result = ExpressionUtil.calculateExpressionInner(value_stack_values, operation_stack_values);
        }catch(Exception e){
            logger.error("splitExpression error", e);
        }
        return result;
    }
    /**
     * 计算表达式内部部分
     * @param _value_stack_values
     * @param _operation_stack_values
     * @return
     * @throws Exception
     */
    private static String calculateExpressionInnerPartForKuohao( 
        List<String> _value_stack_values,
        List<String> _operation_stack_values,
        String  _word_string) throws Exception {
        String result = "";
        try {
            if (_value_stack_values.isEmpty()) {
                return result;
            }
            if (_operation_stack_values.isEmpty()) {
                logger.warn("Operation List is error", result);
                return result;
            }
            // String _word = _word_string;
            // if (!_word.endsWith(")")) {
            //     return result;
            // }
            result = ExpressionUtil.calculateExpressionInnerPartForPriority(_value_stack_values, _operation_stack_values, _word_string);
            result = ExpressionUtil.calculateExpressionInner(_value_stack_values, _operation_stack_values);
        } catch(Exception e){
            logger.error("calculateExpressionInnerPart error", e);
        }
        return result;
    }
    
    /**
     * 计算表达式内部部分， 并且在优先级处理情况。
     * @param _value_stack_values
     * @param _operation_stack_values
     * @param _curosr
     * @return
     * @throws Exception
     */
    private static String calculateExpressionInnerPartForPriority( 
        List<String> _value_stack_values,
        List<String> _operation_stack_values,
        String _word_string) throws Exception {
        String result = "";
        try {
            if (_value_stack_values.isEmpty()) {
                return result;
            }
            String _word = _word_string;            
            if (UtilTools.IsNull(_word)) {
                return result;
            }
            int _priority = ExpressionUtil.getMathSymbolPriority(_word);
            /* 操作符号 */
            if (_operation_stack_values.isEmpty()) {
                _operation_stack_values.add(_word);
                return result;
            }
            /* 处理非括号的操作，出队，用于比较优先级 */
            List<String> _inner_values_list = new ArrayList<>();
            List<String> _inner_operation_list = new ArrayList<>(); 
            
            int _inner_priority = _priority;

            /* 先扫描表达式 */
            while(!_operation_stack_values.isEmpty()) {
                /* 出栈 */
                String fore_operation = _operation_stack_values.remove(_operation_stack_values.size() - 1);
                if (fore_operation.equals("(")) {
                    _operation_stack_values.add(fore_operation);
                    _operation_stack_values.add(_word);
                    break;
                }
                /* 获得操作符优先级, 获得优先级别，在比较之前的操作符号 */
                boolean isOldOperation = false;
                int fore_priority = ExpressionUtil.getMathSymbolPriority(fore_operation); 
                while( (_inner_priority > fore_priority) &&
                      !_value_stack_values.isEmpty() ) {
                    isOldOperation = false;    
                    /* 出栈 */                    
                    String value2 = _value_stack_values.remove(_value_stack_values.size() -1);
                    if (_value_stack_values.isEmpty() && !fore_operation.equals("!")) {
                        break;
                    }
                    if (fore_operation.equals("!") ){
                        _inner_values_list.add(0, value2);
                        _inner_operation_list.add(0, fore_operation);
                        _priority = fore_priority;                        
                        continue;
                    }
                    _inner_operation_list.add(0, fore_operation);
                    _inner_values_list.add(0, value2);
                    String value1 = _value_stack_values.remove(_value_stack_values.size() -1);
                    _inner_values_list.add(0, value1); 
                    _inner_priority = fore_priority;
                    if (_operation_stack_values.isEmpty()) {
                        break;
                    }
                    fore_operation = _operation_stack_values.remove(_operation_stack_values.size() - 1);
                    isOldOperation = true;
                    fore_priority = ExpressionUtil.getMathSymbolPriority(fore_operation);
                }
                if (isOldOperation) {
                    _operation_stack_values.add(fore_operation);
                }

                /* 运算 */
                if (!_inner_operation_list.isEmpty()) {
                    while(!_inner_operation_list.isEmpty()) {
                        String value01 = _inner_values_list.remove(0); 
                        String opera01 = _inner_operation_list.remove(0); 
                        if (opera01.equals("!")){
                            result = ExpressionUtil.calculateExpression(value01, "", opera01);    
                            _value_stack_values.add(result); 
                            continue;
                        }
                        String value02 = _inner_values_list.remove(0);                     
                        result = ExpressionUtil.calculateExpression(value01, value02, opera01);    
                        /* 入队 */
                        _value_stack_values.add(result); 
                    }
                } else {
                    _operation_stack_values.add(fore_operation);
                }
                if (_inner_operation_list.isEmpty()){
                    _operation_stack_values.add(_word_string);
                    break;
                }
            }

        } catch(Exception e){
            logger.error("calculateExpressionInnerPart error", e);
        }
        return result;
    }
    /**
     * 计算表达式，用于全部表达式的计算
     * @param _value_stack_values
     * @param _operation_stack_values 
     * @return
     */
    private static String calculateExpressionInner(
        List<String> _value_stack_values,
        List<String> _operation_stack_values
        ) throws Exception {
        String result = "";
        try {
            if (_value_stack_values.isEmpty()) {
                return result;
            }
            if (_operation_stack_values.isEmpty()) {
                return result;
            }            
            while(!_operation_stack_values.isEmpty()) {           
                String _word_ = _operation_stack_values.get(_operation_stack_values.size() - 1);  
                if (_word_.equals(")")) {
                    _operation_stack_values.remove(_operation_stack_values.size() - 1);
                    continue;
                }
                if (_word_.equals("(")) {
                    _operation_stack_values.remove(_operation_stack_values.size() - 1);
                    break;                    
                }
                /* 级别最高的Prority */
                int _min_priority = -1;  
                /* 进行循环处理, 处理表达式， 现货的优先级，级别, 从_operation_stack_values 最后一个项目 */
                _min_priority = ExpressionUtil.getMinPriority(_operation_stack_values);
                /* 获取最开始的问题，0， 或者最近的“（ */
                List<Integer> startCursors = ExpressionUtil.getStartCursor(_operation_stack_values, _value_stack_values);
                if (startCursors.isEmpty()){
                    return result;
                }
                Integer _operation_cursor_obj = startCursors.get(0);
                Integer _value_cursor_obj = startCursors.get(1);
                if (_value_cursor_obj > 0) {
                    _value_cursor_obj--;
                }
                int _operation_cursor = _operation_cursor_obj.intValue();
                int _value_cursor = _value_cursor_obj.intValue();
                while((_operation_cursor < _operation_stack_values.size()) && 
                    ( !_operation_stack_values.isEmpty())  ) {
                    String word = _operation_stack_values.get(_operation_cursor);
                    if (UtilTools.IsNull(word)) {
                        _operation_cursor++;
                        continue;
                    }
                    if (word.equals("(")) {
                        _operation_cursor++;
                        _value_cursor++;
                        continue;                    
                    }
                    word = _operation_stack_values.remove(_operation_cursor);
                    if (_value_stack_values.isEmpty()) {
                        break;
                    }
                    int tmp_value_cursor = _value_cursor;
                    String value01 = _value_stack_values.remove(_value_cursor);
                    String value02 = "";
                    if (!word.equals("!")) {
                        if (_value_stack_values.isEmpty()) {
                            break;
                        }                    
                        value02 = _value_stack_values.remove(_value_cursor);
                    }
                    int tmp_priority = ExpressionUtil.getMathSymbolPriority(word);
                    if ((tmp_priority<= _min_priority) || (tmp_priority < 0)) {
                        String _result = ExpressionUtil.calculateExpression(value01, value02, word);
                        _value_stack_values.add(tmp_value_cursor, _result);
                        continue;
                    } else {
                        _operation_stack_values.add(_operation_cursor, word);
                        if (!word.equals("!")) {
                            _value_stack_values.add(_value_cursor, value02);
                        }
                        _value_stack_values.add(_value_cursor, value01);
                    }
                    _operation_cursor++;
                    _value_cursor++;
                }
            }
            result = _value_stack_values.get(0);
        } catch(Exception e){
            logger.error("calculateExpressionInner error", e);
        }
        return result;
    }
    /**
     * 从Stack 中获得最高优先级的操作符号
     * @param mathSymbols
     * @return
     */
    private final static int getMinPriority(List<String> mathSymbols){
        int minPriority = -1;
        try {
            
            int cursor = mathSymbols.size() - 1;
            while((cursor >= 0) && 
                (cursor < mathSymbols.size()) &&
                (!mathSymbols.get(cursor).equals("("))){
                String word = mathSymbols.get(cursor);
                if (UtilTools.IsNull(word)) {
                    cursor--;
                    continue;
                }
                int tmp_priority = ExpressionUtil.getMathSymbolPriority(word);
                if (minPriority < 0) {
                    minPriority = tmp_priority;
                } else if (tmp_priority < minPriority) {
                    minPriority = tmp_priority;
                }
                cursor--;           
            }
            return minPriority;
        }catch(Exception e){
            logger.error("getMinPriority error", e);
        }
        return -1;
    }

    /**
     * 获取表达式中最开始的光标：0， 或者最近的“（”
     * @param mathSymbols
     * @param valueSymbols
     * @return
     */
    private final static List<Integer> getStartCursor(
        List<String> mathSymbols,
        List<String> valueSymbols){
        List<Integer> startCursors = new ArrayList<Integer>();
        try { 

            int tmp_cursor = mathSymbols.size()- 1;
            int _value_cursor = valueSymbols.size() - 1;
            /**
             * 
             * 从后面开始，找到最近的"(" 或者0
             * 
             */
            while((tmp_cursor >= 0) && 
                (tmp_cursor < mathSymbols.size()) &&
                (!mathSymbols.get(tmp_cursor).equals("("))){
                String word = mathSymbols.get(tmp_cursor);
                if (UtilTools.IsNull(word)) {
                    tmp_cursor--;
                    continue;
                }
                if (word.equals("(")){
                    break;
                }
                if (!word.equals("!")){
                    _value_cursor--;
                }                
                tmp_cursor--;
            }
                
            if ( tmp_cursor < 0 ) {
                tmp_cursor = 0;
                _value_cursor = 0;
            }
            startCursors.add( tmp_cursor);
            startCursors.add( _value_cursor);
            return startCursors;
        }catch(Exception e){
            logger.error("getStartCursor error", e);
        }
        return new ArrayList<Integer>();
    }

    /**
     * 计算表达式
     * @param expres01
     * @param expres02
     * @param operaString
     * @return
     */
    public final static String calculateExpression(
        String expres01,
        String expres02,
        String operaString) {
        String result = "";
        try {
            if (UtilTools.IsNull(expres01) || UtilTools.IsNull(expres02) || UtilTools.IsNull(operaString)) {
                logger.warn("calculateExpression expres01 or expres02 or operaString is null or empty");
                return result;
            }            
            switch (operaString) {
                case "+":{
                        double value01 = Double.parseDouble(expres01);
                        double value02 = Double.parseDouble(expres02);
                        result = String.valueOf(value01 + value02);                    
                    }
                    break;
                case "-":{
                        double value01 = Double.parseDouble(expres01);
                        double value02 = Double.parseDouble(expres02);                        
                        result = String.valueOf(value01 - value02);                    
                    }
                    break;
                case "*":{
                        double value01 = Double.parseDouble(expres01);
                        double value02 = Double.parseDouble(expres02);
                        result = String.valueOf(value01 * value02);                    
                    }
                    break;
                case "/":{
                        double value01 = Double.parseDouble(expres01);
                        double value02 = Double.parseDouble(expres02);
                        result = String.valueOf(value01 / value02);                    
                    }
                    break;
                case "%":{
                        double value01 = Double.parseDouble(expres01);
                        double value02 = Double.parseDouble(expres02);
                        result = String.valueOf(value01 % value02);                    
                    }
                    break;
                case "^":{
                        double value01 = Double.parseDouble(expres01);
                        double value02 = Double.parseDouble(expres02);
                        result = String.valueOf(Math.pow(value01, value02));                    
                    }
                    break;
                case "&&":{
                        boolean value01 = Boolean.parseBoolean(expres01);
                        boolean value02 = Boolean.parseBoolean(expres02);
                        result = String.valueOf(value01 && value02);                    
                    }
                    break;
                case "||":{
                        boolean value01 = Boolean.parseBoolean(expres01);
                        boolean value02 = Boolean.parseBoolean(expres02);
                        result = String.valueOf(value01 || value02);                    
                    }
                    break;
                case "!=":{
                        if ((expres01.equals("true") || expres01.equals("false")) && 
                            (expres02.equals("true") || expres02.equals("false"))) {
                            Boolean value01 = Boolean.parseBoolean(expres01);
                            Boolean value02 = Boolean.parseBoolean(expres02);
                            result = String.valueOf(value01 != value02);                    
                        } else {
                            Double value01 = Double.parseDouble(expres01);
                            Double value02 = Double.parseDouble(expres02);
                            result = String.valueOf( Math.abs(value01-value02) > ExpressionUtil.MATH_SYMBOL_EPSIILON );                    
                        }                  
                    }
                    break;
                case "==":{                        
                        if ((expres01.equals("true") || expres01.equals("false")) && 
                            (expres02.equals("true") || expres02.equals("false"))) {
                            Boolean value01 = Boolean.parseBoolean(expres01);
                            Boolean value02 = Boolean.parseBoolean(expres02);
                            result = String.valueOf(value01 != value02);                    
                        } else {
                            Double value01 = Double.parseDouble(expres01);
                            Double value02 = Double.parseDouble(expres02);
                            result = String.valueOf( Math.abs(value01-value02) <= ExpressionUtil.MATH_SYMBOL_EPSIILON );                    
                        }                    
                    }
                    break;
                case ">>":{
                        int value01 = Integer.parseInt(expres01);
                        int value02 = Integer.parseInt(expres02);
                        result = String.valueOf(value01 >> value02);                    
                    }
                    break;
                case "<<":{
                        int value01 = Integer.parseInt(expres01);
                        int value02 = Integer.parseInt(expres02);
                        result = String.valueOf(value01 << value02);                    
                    }
                    break;
                case ">>=":{
                        int value01 = Integer.parseInt(expres01);
                        int value02 = Integer.parseInt(expres02);
                        result = String.valueOf(value01 >>= value02);                    
                    }
                    break;
                case "<<=":{
                        int value01 = Integer.parseInt(expres01);
                        int value02 = Integer.parseInt(expres02);
                        result = String.valueOf(value01 <<= value02);                    
                    }
                    break;
                case ">":{
                        int value01 = Integer.parseInt(expres01);
                        int value02 = Integer.parseInt(expres02);
                        result = String.valueOf(value01 > value02);                    
                    }
                    break;
                case "<":{
                        int value01 = Integer.parseInt(expres01);
                        int value02 = Integer.parseInt(expres02);
                        result = String.valueOf(value01 < value02);                    
                    }
                    break;
                case ">=":{
                        int value01 = Integer.parseInt(expres01);
                        int value02 = Integer.parseInt(expres02);
                        result = String.valueOf(value01 >= value02);                    
                    }
                    break;
                case "<=":{
                        int value01 = Integer.parseInt(expres01);
                        int value02 = Integer.parseInt(expres02);
                        result = String.valueOf(value01 <= value02);                    
                    }
                    break;
                case "|":{
                        int value01 = Integer.parseInt(expres01);
                        int value02 = Integer.parseInt(expres02);
                        result = String.valueOf(value01 | value02);                    
                    }
                    break;
                case "&":{
                        int value01 = Integer.parseInt(expres01);
                        int value02 = Integer.parseInt(expres02);
                        result = String.valueOf(value01 & value02);                    
                    }
                    break;
                case "^=":{
                        Double value01 = Double.parseDouble(expres01);
                        Double value02 = Double.parseDouble(expres02);
                        value01 = Math.pow(value01, value02);
                        result = String.valueOf(value01);                    
                    }
                    break;
                case "!":{
                        boolean value01 = Boolean.parseBoolean(expres01);
                        result = String.valueOf(!value01);                    
                    }
                    break;
                default:
                    break;
            }
        } catch (Exception e) {
            logger.error("calculateExpression error", e);
        }
        return result;
    }

    /**
     * 获取操作符优先级
     * @param operation
     * @return
     */
    public static int getPriority(String operation) {
        int priority = 0;
        if (operation.equals("+") || operation.equals("-")) {
            priority = 1;
        } else if (operation.equals("*") || operation.equals("/")) {
            priority = 2;
        }
        return priority;
    }

}
